﻿/*
 * Author: Mattan360
 * Contributors:
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using Irrlicht.Net.Video;
using Irrlicht.Net.Core;
using Irrlicht.Net.Scene;

namespace Irrlicht.Net.Scene
{
    public sealed class SceneManager : ReferenceCounted
    {
        #region PInvoke
        [DllImport(@"IrrlichtW.dll", CallingConvention = CallingConvention.Cdecl)]
        internal static extern bool Scene_DrawAll(IntPtr scmgr);

        [DllImport(@"IrrlichtW.dll", CallingConvention = CallingConvention.Cdecl)]
        internal static extern IntPtr Scene_getMesh(IntPtr scmgr,
            [MarshalAs(UnmanagedType.AnsiBStr)]
            string path);
        [DllImport(@"IrrlichtW.dll", CallingConvention = CallingConvention.Cdecl)]
        internal static extern IntPtr Scene_GetVideoDriver(IntPtr scmgr);

        [DllImport(@"IrrlichtW.dll", CallingConvention = CallingConvention.Cdecl)]
        //Add Animated Mesh
        internal static extern IntPtr Scene_addAnimatedMeshSceneNode(IntPtr scmgr, IntPtr mesh, int id,
            float xPos, float yPos, float zPos, float xRot, float yRot, float zRot, float xScale, float yScale, float zScale);
        [DllImport(@"IrrlichtW.dll", CallingConvention = CallingConvention.Cdecl)]
        // Add Sphere
        internal static extern IntPtr Scene_addSphereSceneNode(IntPtr scmgr, float radius, int polyCount, IntPtr parent, int id, 	
            float xPos, float yPos, float zPos, float xRot, float yRot, float zRot, float xScale, float yScale, float zScale);
        [DllImport(@"IrrlichtW.dll", CallingConvention = CallingConvention.Cdecl)]
        // Add Cube
        internal static extern IntPtr Scene_addCubeSceneNode(IntPtr scmgr, float size, IntPtr parent, int id,
            float xPos, float yPos, float zPos, float xRot, float yRot, float zRot, float xScale, float yScale, float zScale);
        [DllImport(@"IrrlichtW.dll", CallingConvention = CallingConvention.Cdecl)]
        //Add Terrain
        internal static extern IntPtr Scene_addTerrainSceneNode(IntPtr scmgr,
            [MarshalAs(UnmanagedType.AnsiBStr)]
            string path,
            int id, float xPos, float yPos, float zPos, float xRot, float yRot, float zRot, float xScale, float yScale, float zScale, int maxLod);
        [DllImport(@"IrrlichtW.dll", CallingConvention = CallingConvention.Cdecl)]
        //Add FPS Camera
        internal static extern IntPtr Scene_addCameraSceneNodeFPS(IntPtr scmgr, int id, float rotateSpeed, float moveSpeed,
            bool noVerticalMovement, float jumpSpeed, bool invertMouse, bool makeActive);
        [DllImport(@"IrrlichtW.dll", CallingConvention = CallingConvention.Cdecl)]
        //Add Sky Dome
        internal static extern IntPtr Scene_addSkyDomeSceneNode(IntPtr scmgr, IntPtr tex, uint hRes, uint vRes,
            float texturePercentage, float spherePercentage, float radius, int id);
        //Add Octree
        [DllImport(@"IrrlichtW.dll", CallingConvention = CallingConvention.Cdecl)]
        internal static extern IntPtr Scene_AddOctree(IntPtr scmgr, IntPtr mesh,
            int id, int minimalPolysPerNode, bool alsoAddIfMeshPointerZero);
        [DllImport(@"IrrlichtW.dll", CallingConvention = CallingConvention.Cdecl)]
        internal static extern IntPtr Scene_AddOctreeAnimated(IntPtr scmgr, IntPtr mesh,
	        int id, int minimalPolysPerNode, bool alsoAddIfMeshPointerZero);

        // Create Animators
        [DllImport(@"IrrlichtW.dll", CallingConvention = CallingConvention.Cdecl)]
        internal static extern IntPtr Scene_createFlyCircleAnimator(IntPtr scmgr, float xPos, float yPos, float zPos,
            float radius, float speed, float xDir, float yDir, float zDir, float startPosition, float radiusEllipsoid);
        [DllImport(@"IrrlichtW.dll", CallingConvention = CallingConvention.Cdecl)]
        internal static extern IntPtr Scene_createFlyStraightAnimator(IntPtr scmgr, float xStart, float yStart, float zStart,
            float xEnd, float yEnd, float zEnd, uint timeForWay, bool loop, bool pingpong);
        #endregion

        internal static SceneManager CreateSceneManager(IntPtr pointer)
        {
            SceneManager manager = new SceneManager();
            manager.pointer = pointer;
            return manager;
        }

        public VideoDriver VideoDriver
        {
            get
            {
                return VideoDriver.CreateVideoDriver(Scene_GetVideoDriver(pointer));
            }
        }

        public void DrawAll()
        {
            Scene_DrawAll(pointer);
        }

        public AnimatedMesh GetMesh(string path)
        {
            return AnimatedMesh.CreateAnimatedMesh(Scene_getMesh(pointer, path));
        }

        public AnimatedMeshSceneNode AddAnimatedMeshSceneNode(AnimatedMesh mesh)
        {
            return new AnimatedMeshSceneNode(Scene_addAnimatedMeshSceneNode(pointer, mesh.pointer, -1, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f));
        }

        public AnimatedMeshSceneNode AddAnimatedMeshSceneNode(AnimatedMesh mesh, int id)
        {
            return new AnimatedMeshSceneNode(Scene_addAnimatedMeshSceneNode(pointer, mesh.pointer, id, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f));
        }

        public AnimatedMeshSceneNode AddAnimatedMeshSceneNode(AnimatedMesh mesh, Vector3D pos)
        {
            return new AnimatedMeshSceneNode(Scene_addAnimatedMeshSceneNode(pointer, mesh.pointer, -1, pos.x, pos.y, pos.z, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f));
        }

        public AnimatedMeshSceneNode AddAnimatedMeshSceneNode(AnimatedMesh mesh, Vector3D pos, Vector3D rot)
        {
            return new AnimatedMeshSceneNode(Scene_addAnimatedMeshSceneNode(pointer, mesh.pointer, -1, pos.x, pos.y, pos.z, rot.x, rot.y, rot.z, 1.0f, 1.0f, 1.0f));
        }

        public AnimatedMeshSceneNode AddAnimatedMeshSceneNode(AnimatedMesh mesh, Vector3D pos, Vector3D rot, Vector3D scale)
        {
            return new AnimatedMeshSceneNode(Scene_addAnimatedMeshSceneNode(pointer, mesh.pointer, -1,
                pos.x, pos.y, pos.z, rot.x, rot.y, rot.z, scale.x, scale.y, scale.z));
        }

        public AnimatedMeshSceneNode AddAnimatedMeshSceneNode(AnimatedMesh mesh, Vector3D pos, Vector3D rot, Vector3D scale, int id)
        {
            return new AnimatedMeshSceneNode(Scene_addAnimatedMeshSceneNode(pointer, mesh.pointer, id,
                pos.x, pos.y, pos.z, rot.x, rot.y, rot.z, scale.x, scale.y, scale.z));
        }

        public MeshSceneNode AddCubeSceneNode()
        {
            return new MeshSceneNode(Scene_addCubeSceneNode(pointer, 10.0f, (IntPtr)0, -1, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f));
        }

        public MeshSceneNode AddCubeSceneNode(float size)
        {
            return new MeshSceneNode(Scene_addCubeSceneNode(pointer, size, (IntPtr) 0, -1, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f));
        }

        public MeshSceneNode AddCubeSceneNode(float size, SceneNode parent)
        {
            return new MeshSceneNode(Scene_addCubeSceneNode(pointer, size, parent.pointer, -1, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f));
        }

        public MeshSceneNode AddCubeSceneNode(float size, SceneNode parent, int id)
        {
            return new MeshSceneNode(Scene_addCubeSceneNode(pointer, size, parent.pointer, id, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f));
        }

        public MeshSceneNode AddCubeSceneNode(float size, SceneNode parent, int id, Vector3D pos)
        {
            return new MeshSceneNode(Scene_addCubeSceneNode(pointer, size, parent.pointer, id, pos.X, pos.Y, pos.Z, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f));
        }

        public MeshSceneNode AddCubeSceneNode(float size, SceneNode parent, int id, Vector3D pos, Vector3D rot)
        {
            return new MeshSceneNode(Scene_addCubeSceneNode(pointer, size, parent.pointer, id, pos.X, pos.Y, pos.Z, rot.X, rot.Y, rot.Z, 1.0f, 1.0f, 1.0f));
        }

        public MeshSceneNode AddCubeSceneNode(float size, SceneNode parent, int id, Vector3D pos, Vector3D rot, Vector3D scale)
        {
            return new MeshSceneNode(Scene_addCubeSceneNode(pointer, size, parent.pointer, id, pos.X, pos.Y, pos.Z, rot.X, rot.Y, rot.Z, scale.X, scale.Y, scale.Z));
        }

        public MeshSceneNode AddSphereSceneNode()
        {
            return new MeshSceneNode(Scene_addSphereSceneNode(pointer, 5.0f, 16, (IntPtr)0, -1, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f));
        }

        public MeshSceneNode AddSphereSceneNode(float radius)
        {
            return new MeshSceneNode(Scene_addSphereSceneNode(pointer, radius, 16, (IntPtr)0, -1, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f));
        }

        public MeshSceneNode AddSphereSceneNode(float radius, int polyCount)
        {
            return new MeshSceneNode(Scene_addSphereSceneNode(pointer, radius, polyCount, (IntPtr) 0, -1, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f));
        }

        public MeshSceneNode AddSphereSceneNode(float radius, int polyCount, SceneNode parent)
        {
            return new MeshSceneNode(Scene_addSphereSceneNode(pointer, radius, polyCount, parent.pointer, -1, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f));
        }

        public MeshSceneNode AddSphereSceneNode(float radius, int polyCount, SceneNode parent, int id)
        {
            return new MeshSceneNode(Scene_addSphereSceneNode(pointer, radius, polyCount, parent.pointer, id, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f));
        }

        public MeshSceneNode AddSphereSceneNode(float radius, int polyCount, SceneNode parent, int id, Vector3D pos)
        {
            return new MeshSceneNode(Scene_addSphereSceneNode(pointer, radius, polyCount, parent.pointer, id, pos.X, pos.Y, pos.Z, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f));
        }

        public MeshSceneNode AddSphereSceneNode(float radius, int polyCount, SceneNode parent, int id, Vector3D pos, Vector3D rot)
        {
            return new MeshSceneNode(Scene_addSphereSceneNode(pointer, radius, polyCount, parent.pointer, id, pos.X, pos.Y, pos.Z, rot.X, rot.Y, rot.Z, 1.0f, 1.0f, 1.0f));
        }

        public MeshSceneNode AddSphereSceneNode(float radius, int polyCount, SceneNode parent, int id, Vector3D pos, Vector3D rot, Vector3D scale)            
        {
            return new MeshSceneNode(Scene_addSphereSceneNode(pointer, radius, polyCount, parent.pointer, id, pos.X, pos.Y, pos.Z, rot.X, rot.Y, rot.Z, scale.X, scale.Y, scale.Z));
        }

        public TerrainSceneNode AddTerrainSceneNode(string path)
        {
            return new TerrainSceneNode(Scene_addTerrainSceneNode(pointer, path, -1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 5));
        }

        public TerrainSceneNode AddTerrainSceneNode(string path, int maxLod)
        {
            return new TerrainSceneNode(Scene_addTerrainSceneNode(pointer, path, -1, 0, 0, 0, 0, 0, 0, 1, 1, 1, maxLod));
        }

        public TerrainSceneNode AddTerrainSceneNode(string path, int maxLod, Vector3D pos)
        {
            return new TerrainSceneNode(Scene_addTerrainSceneNode(pointer, path, -1, pos.x, pos.y, pos.z, 0, 0, 0, 1, 1, 1, maxLod));
        }

        public TerrainSceneNode AddTerrainSceneNode(string path, int maxLod, Vector3D pos, Vector3D rot, Vector3D scale)
        {
            return new TerrainSceneNode(Scene_addTerrainSceneNode(pointer, path, -1,
                pos.x, pos.y, pos.z, rot.x, rot.y, rot.z, scale.x, scale.y, scale.z, maxLod));
        }

        public CameraSceneNode AddCameraSceneNodeFPS()
        {
            return new CameraSceneNode(Scene_addCameraSceneNodeFPS(pointer, -1, 100, 0.5f, false, 0, false, true));
        }

        public CameraSceneNode AddCameraSceneNodeFPS(int id)
        {
            return new CameraSceneNode(Scene_addCameraSceneNodeFPS(pointer, id, 100, 0.5f, false, 0, false, true));
        }

        public CameraSceneNode AddCameraSceneNodeFPS(float rotationSpeed, float moveSpeed, float jumpSpeed)
        {
            return new CameraSceneNode(Scene_addCameraSceneNodeFPS(pointer, -1, rotationSpeed, moveSpeed, false, jumpSpeed, false, true));
        }

        public CameraSceneNode AddCameraSceneNodeFPS(int id, float rotationSpeed, float moveSpeed, float jumpSpeed)
        {
            return new CameraSceneNode(Scene_addCameraSceneNodeFPS(pointer, id, rotationSpeed, moveSpeed, false, jumpSpeed, false, true));
        }

        public CameraSceneNode AddCameraSceneNodeFPS(int id, float rotationSpeed, float moveSpeed, float jumpSpeed, bool noVerticalMovement, bool invertMouse)
        {
            return new CameraSceneNode(Scene_addCameraSceneNodeFPS(pointer, id, rotationSpeed, moveSpeed, noVerticalMovement, jumpSpeed, invertMouse, true));
        }

        public CameraSceneNode AddCameraSceneNodeFPS(int id, float rotationSpeed, float moveSpeed, float jumpSpeed, bool noVerticalMovement, bool invertMouse, bool makeActive)
        {
            return new CameraSceneNode(Scene_addCameraSceneNodeFPS(pointer, id, rotationSpeed, moveSpeed, noVerticalMovement, jumpSpeed, invertMouse, makeActive));
        }

        public SceneNode AddSkyDomeSceneNode(Texture texture)
        {
            return new SceneNode(Scene_addSkyDomeSceneNode(pointer, texture.pointer, 16, 8, 0.9f, 2, 1000, -1));
        }

        public MeshSceneNode AddOctree(AnimatedMesh mesh)
        {
            return new MeshSceneNode(Scene_AddOctreeAnimated(pointer, mesh.pointer, -1, 512, false));
        }

        public MeshSceneNode AddOctree(AnimatedMesh mesh, int minimalPolysPerNode)
        {
            return new MeshSceneNode(Scene_AddOctreeAnimated(pointer, mesh.pointer, -1, minimalPolysPerNode, false));
        }

        public MeshSceneNode AddOctree(AnimatedMesh mesh, int id, int minimalPolysPerNode, bool alsoAddIfMeshPointerZero)
        {
            return new MeshSceneNode(Scene_AddOctreeAnimated(pointer, mesh.pointer, id, minimalPolysPerNode, alsoAddIfMeshPointerZero));
        }

        public MeshSceneNode AddOctree(Mesh mesh)
        {
            return new MeshSceneNode(Scene_AddOctree(pointer, mesh.pointer, -1, 256, false));
        }

        public MeshSceneNode AddOctree(Mesh mesh, int minimalPolysPerNode)
        {
            return new MeshSceneNode(Scene_AddOctree(pointer, mesh.pointer, -1, minimalPolysPerNode, false));
        }

        public MeshSceneNode AddOctree(Mesh mesh, int id, int minimalPolysPerNode, bool alsoAddIfMeshPointerZero)
        {
            return new MeshSceneNode(Scene_AddOctree(pointer, mesh.pointer, id, minimalPolysPerNode, alsoAddIfMeshPointerZero));
        }

        public SceneNodeAnimator CreateFlyCircleAnimator()
        {
            return new SceneNodeAnimator(Scene_createFlyCircleAnimator(pointer, 0.0f, 0.0f, 0.0f, 100.0f, 0.001f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f));
        }

        public SceneNodeAnimator CreateFlyCircleAnimator(Vector3D center)
        {
            return new SceneNodeAnimator(Scene_createFlyCircleAnimator(pointer, center.X, center.Y, center.Z, 100.0f, 0.001f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f));
        }

        public SceneNodeAnimator CreateFlyCircleAnimator(Vector3D center, float radius)
        {
            return new SceneNodeAnimator(Scene_createFlyCircleAnimator(pointer, center.X, center.Y, center.Z, radius, 0.001f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f));
        }

        public SceneNodeAnimator CreateFlyCircleAnimator(Vector3D center, float radius, float speed)
        {
            return new SceneNodeAnimator(Scene_createFlyCircleAnimator(pointer, center.X, center.Y, center.Z, radius, speed, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f));
        }

        public SceneNodeAnimator CreateFlyCircleAnimator(Vector3D center, float radius, float speed, Vector3D direction)
        {
            return new SceneNodeAnimator(Scene_createFlyCircleAnimator(pointer, center.X, center.Y, center.Z, radius, speed, direction.X, direction.Y, direction.Z, 0.0f, 0.0f));
        }

        public SceneNodeAnimator CreateFlyCircleAnimator(Vector3D center, float radius, float speed, Vector3D direction, float startPosition)
        {
            return new SceneNodeAnimator(Scene_createFlyCircleAnimator(pointer, center.X, center.Y, center.Z, radius, speed, direction.X, direction.Y, direction.Z, startPosition, 0.0f));
        }

        public SceneNodeAnimator CreateFlyCircleAnimator(Vector3D center, float radius, float speed, Vector3D direction, float startPosition, float radiusEllipsoid)
        {
            return new SceneNodeAnimator(Scene_createFlyCircleAnimator(pointer, center.X, center.Y, center.Z, radius, speed, direction.X, direction.Y, direction.Z, startPosition, radiusEllipsoid));
        }

        public SceneNodeAnimator CreateFlyStraightAnimator(Vector3D startPoint, Vector3D endPoint, uint timeForWay)
        {
            return new SceneNodeAnimator(Scene_createFlyStraightAnimator(pointer, startPoint.X, startPoint.Y, startPoint.Z, endPoint.X, endPoint.Y, endPoint.Z, timeForWay, false, false));
        }

        public SceneNodeAnimator CreateFlyStraightAnimator(Vector3D startPoint, Vector3D endPoint, uint timeForWay, bool loop)
        {
            return new SceneNodeAnimator(Scene_createFlyStraightAnimator(pointer, startPoint.X, startPoint.Y, startPoint.Z, endPoint.X, endPoint.Y, endPoint.Z, timeForWay, loop, false));
        }

        public SceneNodeAnimator CreateFlyStraightAnimator(Vector3D startPoint, Vector3D endPoint, uint timeForWay, bool loop, bool pingpong)
        {
            return new SceneNodeAnimator(Scene_createFlyStraightAnimator(pointer, startPoint.X, startPoint.Y, startPoint.Z, endPoint.X, endPoint.Y, endPoint.Z, timeForWay, loop, pingpong));
        }
    }
}
